package game;

import input.Keys;

import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.game.LayerManager;
import javax.microedition.lcdui.game.Sprite;
import javax.microedition.lcdui.game.TiledLayer;

import player.Player;

import sound.SoundGenerator;
import timer.Timer;

import math.Vector2D;
import ImageLoader.ImageLoader;
import camera.Camera;
import fishGenerator.*;
import game.GameMIDlet;
import gameEntities.*;
import constants.Constants;

/**
 * The Class MyGameCanvas is the main Game-class.
 * It contains the game-loop and enables all of
 * the games functionality.
 */
public class MyGameCanvas extends GameCanvas implements Runnable {
	
	private LayerManager layerManager;
	private TiledLayer backgrounds[] = new TiledLayer[Constants.POINTSFORNEXTLEVEL.length];		//Die Levels
	
	private Shore shore;
	private Angler angler;
	private Bait bait;
	
	private int waterAnimCounter = 0;
	private int waterSpeedCounter = 0;
	private int waterAnimTileID;
	private int seegrasAnimCounter = 0;
	private int seegrasSpeedCounter = 0;
	private int seegrasAnimTileID;
	
	private Camera camera;
	
	private SmallFishGenerator rightSmallFishGenerator;
	private SmallFishGenerator leftSmallFishGenerator;
	private MediumFishGenerator rightMediumFishGenerator;
	private MediumFishGenerator leftMediumFishGenerator;
	private BigFishGenerator rightBigFishGenerator;
	private BigFishGenerator leftBigFishGenerator;
	private SharkGenerator rightSharkgenerator;
	private SharkGenerator leftSharkgenerator;
	
	private GameEntityManager gem;
	
	private Keys keys;
	
	private boolean canCast;
	
	private boolean paused = false;
	
	private SoundGenerator soundGenerator;
	
	private Player player;
	
	private int level = 1;
	
	private GameMIDlet gameMIDlet;
	
	/**
	 * Instantiates a new game canvas and generates all resources.
	 */
	protected MyGameCanvas(GameMIDlet gameMIDlet) {
		super(true);
		
		this.gameMIDlet = gameMIDlet;
		layerManager = new LayerManager();
		this.setFullScreenMode(true);

		for(int i = 0; i < backgrounds.length; i++)
			backgrounds[i] = createBackground("/background" + (i+1) +".png");
		
		camera = new Camera(0, 0, 0, Constants.MAPWIDTH - getWidth(), 0, Constants.MAPHEIGHT - getHeight(), getWidth(), getHeight());
		
		gem = new GameEntityManager(layerManager);

		for(int i = 0; i < backgrounds.length; i++) {
			layerManager.append(backgrounds[i]);
			if(i > 0) 
				backgrounds[i].setVisible(false);
		}

		createShore();
		createAngler();
		
		rightSmallFishGenerator = new SmallFishGenerator(new Vector2D((float)Constants.MAPWIDTH + 20f, Constants.WATERLEVEL), Constants.SMALLFISHDEPTHRANGE, Constants.SMALLFISHDEPTHSTART, false, Constants.SMALLFISHSPEED, gem);
		leftSmallFishGenerator = new SmallFishGenerator(new Vector2D(-20.0f, Constants.WATERLEVEL), Constants.SMALLFISHDEPTHRANGE, Constants.SMALLFISHDEPTHSTART, true, Constants.SMALLFISHSPEED, gem);
		
		rightMediumFishGenerator = new MediumFishGenerator(new Vector2D((float)Constants.MAPWIDTH + 20f, Constants.WATERLEVEL), Constants.MEDIUMFISHDEPTHRANGE, Constants.MEDIUMFISHDEPTHSTART, false, Constants.MEDIUMFISHSPEED, gem);
		leftMediumFishGenerator = new MediumFishGenerator(new Vector2D(-20.0f, Constants.WATERLEVEL), Constants.MEDIUMFISHDEPTHRANGE, Constants.MEDIUMFISHDEPTHSTART, true, Constants.MEDIUMFISHSPEED, gem);
		
		rightBigFishGenerator = new BigFishGenerator(new Vector2D((float)Constants.MAPWIDTH + 20f, Constants.WATERLEVEL), Constants.BIGFISHDEPTHRANGE, Constants.BIGFISHDEPTHSTART, false, Constants.BIGFISHSPEED, gem);
		leftBigFishGenerator = new BigFishGenerator(new Vector2D(-20.0f, Constants.WATERLEVEL), Constants.BIGFISHDEPTHRANGE, Constants.BIGFISHDEPTHSTART, true, Constants.BIGFISHSPEED, gem);
		
		rightSharkgenerator = new SharkGenerator(new Vector2D((float)Constants.MAPWIDTH + 20f, Constants.WATERLEVEL), Constants.SHARKDEPTHRANGE, Constants.SHARKDEPTHSTART, false, Constants.SHARKSPEED, gem);
		leftSharkgenerator = new SharkGenerator(new Vector2D(-20.0f, Constants.WATERLEVEL), Constants.SHARKDEPTHRANGE, Constants.SHARKDEPTHSTART, false, Constants.SHARKSPEED, gem);
				
		bait = new Bait(new Sprite(ImageLoader.load("/bait.png")), new Vector2D(60.0f, Constants.WATERLEVEL - 30.0f));
		gem.Add(bait);
		
		keys = new Keys();
		
		canCast = true;
		
		soundGenerator = new SoundGenerator();
		
		player = new Player();
	}
	
	/**
	 * Updates the game screen and plays the tileanimations
	 * of the map
	 *
	 * @param g the Graphics element
	 */
	private void updateGameScreen(Graphics g) {
		keys.Update(getKeyStates());
		
		if(player.IsDead()) {
			gameMIDlet.gameOver(player.GetPoints());
		}
		
		if(paused) {
			if((System.currentTimeMillis()/1000)%2 > 0)		//Dass der Text blinkt
				g.setColor(255, 255, 255);
			g.drawString("PAUSED", 100, 150, Graphics.LEFT|Graphics.TOP);
			
			if (keys.IsReleased(GameCanvas.GAME_A_PRESSED)) {
				continueGame();
			}
		}
		else {
			// input
			if (keys.IsReleased(GameCanvas.GAME_A_PRESSED)) {		//PAUSE
				pauseGame();
			} else if (keys.IsReleased(GameCanvas.GAME_B_PRESSED)) {		//SOUND ON/OFF
				soundGenerator.changeMuteState();
			} else if (keys.IsPressed(GameCanvas.FIRE_PRESSED)) {
				bait.ReelIn();
			} else if (keys.IsReleased(GameCanvas.FIRE_PRESSED)) {
				if (canCast && bait.IsFullyReeledIn()) {
					bait.Cast(new Vector2D(170.0f, -170.0f));
					//bait.Cast(new Vector2D(120.0f, -100.0f));
					canCast = false;
				}
				else if (!canCast && bait.IsFullyReeledIn())
					canCast = true;
			}
	
			// update
			if(waterSpeedCounter++ > Constants.WATERSPEED) {	//Water animation
				waterSpeedCounter = 0;
				waterAnimCounter = (waterAnimCounter+1) % Constants.WATERANIMTILES;
				for(int i = 0; i < backgrounds.length; i++)
					backgrounds[i].setAnimatedTile(waterAnimTileID, waterAnimCounter + 10);
				
				//player.AddTime(1f);				//TODO DELETE THIS LINE!!!!!
				
			}
			if(seegrasSpeedCounter++ > Constants.SEEGRASSPEED) {	//Seegras animation
				switch(seegrasAnimCounter++) {
				case 0: for(int i = 0; i < backgrounds.length; i++)
							backgrounds[i].setAnimatedTile(seegrasAnimTileID, 20);
					break;
				case 1: for(int i = 0; i < backgrounds.length; i++)
							backgrounds[i].setAnimatedTile(seegrasAnimTileID, 21);
					break;
				case 2: for(int i = 0; i < backgrounds.length; i++)
							backgrounds[i].setAnimatedTile(seegrasAnimTileID, 20);
					break;
				case 3: for(int i = 0; i < backgrounds.length; i++)
							backgrounds[i].setAnimatedTile(seegrasAnimTileID, 22);
					break;
				}
				seegrasAnimCounter = seegrasAnimCounter % 4;
				seegrasSpeedCounter = 0;
			}
			
			float deltaTime = Timer.getDeltaTime();
			
			rightSmallFishGenerator.Update(deltaTime * Constants.SMALLFISHAMOUNT);
			leftSmallFishGenerator.Update(deltaTime * Constants.SMALLFISHAMOUNT);
			rightMediumFishGenerator.Update(deltaTime * Constants.MEDIUMFISHAMOUNT);
			leftMediumFishGenerator.Update(deltaTime * Constants.MEDIUMFISHAMOUNT);
			rightBigFishGenerator.Update(deltaTime * Constants.BIGFISHAMOUNT);
			leftBigFishGenerator.Update(deltaTime * Constants.BIGFISHAMOUNT);
			rightSharkgenerator.Update(deltaTime * Constants.SHARKAMOUNT);
			leftSharkgenerator.Update(deltaTime * Constants.SHARKAMOUNT);
			
			soundGenerator.updateSeagulls(deltaTime * Constants.SEAGULLSAMOUNT);
			soundGenerator.updateHorn(deltaTime * Constants.HORNAMOUNT);
			
			gem.Update(deltaTime);
			
			camera.center((int)bait.position.x, (int)bait.position.y);
			
			player.Update(deltaTime);
			
			BaitFishCollider.Collide(gem, bait, player, soundGenerator);
	
			// draw
			//g.fillRect(0, 0, getWidth(), getHeight());
			
			layerManager.setViewWindow(camera.getX(), camera.getY(), getWidth(), getHeight());
			layerManager.paint(g, 0, 0);

			// draw line
			g.setColor(255,255,255);
			g.drawLine(60 - camera.getX(), 152 - camera.getY(), (int)bait.position.x - camera.getX() + 10, (int)bait.position.y - camera.getY() + 10);
			
			// draw text
//			if(level == 2)
//				g.setColor(255, 255, 255);
			g.setFont(Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_MEDIUM));
			g.drawString("Level: " + level + "/" + Constants.POINTSFORNEXTLEVEL.length, 5, 5, Graphics.LEFT|Graphics.TOP);
			g.drawString("Time left: " + (int)player.GetTimeLeft(), 5, 20, Graphics.LEFT|Graphics.TOP);
			g.drawString("Points: " + player.GetPoints(), 5, 35, Graphics.LEFT|Graphics.TOP);
			g.drawString("Sound: " + soundGenerator.getMuteStatusString(), 160, 5, Graphics.LEFT|Graphics.TOP);
			
			if(player.GetPoints() >= Constants.POINTSFORNEXTLEVEL[level-1]) {
				changeLevel(++level);
			}
		}
		flushGraphics();
	}
	
	/**
	 * Loads a background image and creates the background.
	 *
	 * @param sourceImage the source of the background image
	 * @return the tiled layer
	 */
	private TiledLayer createBackground(String sourceImage) {
		TiledLayer bg;
		
		final int bgWidth = Constants.MAPWIDTH/Constants.BGTILESIZE;
		final int bgHeight = Constants.MAPHEIGHT/Constants.BGTILESIZE;
		
		Image bgImage = ImageLoader.load(sourceImage);
		bg = new TiledLayer(bgWidth, bgHeight, bgImage, Constants.BGTILESIZE, Constants.BGTILESIZE);
		int[] cells = {
				 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  6,  8,  8,
				 8,  6,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  3,  1,  4,  8,
				 3,  1,  4,  8,  8,  6,  8,  8,  8,  8,  8,  8,  6,  8,  8,  2,  1,  1,  1,  5,
				 1,  1,  1,  5,  3,  1,  4,  8,  8,  6,  8,  3,  1,  4,  3,  1,  1,  1,  1,  1,
				 1,  1,  1,  1,  1,  1,  1,  5,  3,  1,  7,  1,  1,  1,  1,  1,  1,  1,  1,  1, 
				-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
				 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
				 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
				 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
				 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
				 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
				 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
				 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
				 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
				 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
				 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
				 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
				 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
				 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
				 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
				18, 19, 19, -2, 19, 18, 18, -2, 18, 19, -2, 19, 19, 18, 19, 18, 19, 19, 18, -2 
			};
		
		waterAnimTileID = bg.createAnimatedTile(10);
		seegrasAnimTileID = bg.createAnimatedTile(20);
		
		int column, row;
		
		for (int i = 0; i < cells.length; i++) {
			column = i % bgWidth;
			row = (i - column)/bgWidth;
			bg.setCell(column, row, cells[i]);
		}
		bg.setPosition(0,0);
		
		return bg;
	}

	/**
	 * Creates the shore.
	 */
	private void createShore() {
		shore = new Shore(new Sprite(ImageLoader.load("/shore.png")), new Vector2D(0.f, Constants.WATERLEVEL-2*Constants.BGTILESIZE+6));
		gem.Add(shore);
	}

	/**
	 * Creates the angler.
	 */
	private void createAngler() {
		angler = new Angler(new Sprite(ImageLoader.load("/angler.png")), new Vector2D(38f, Constants.WATERLEVEL-2*Constants.BGTILESIZE+50));
		gem.Add(angler);
	}

	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	public void run() {		//game loop
		while (true) {
			updateGameScreen(getGraphics());
		}
	}

	/**
	 * Pauses the game.
	 */
	public void pauseGame() {
		if(!paused) {
			paused = true;
			soundGenerator.pauseSounds();
			Timer.pauseTimer();
		}
	}

	/**
	 * Continues the paused game.
	 */
	public void continueGame() {
		paused = false;
		soundGenerator.continueSounds();
		Timer.continueTimer();
	}

	/**
	 * Changes the level.
	 *
	 * @param newLevel the new level
	 */
	private void changeLevel(int newLevel) {
		backgrounds[newLevel-2].setVisible(false);
		backgrounds[newLevel-1].setVisible(true);
	}
}
